package com.dailywork.basepro.stroper;

import org.apache.commons.lang3.StringUtils;
import org.apache.xalan.xsltc.compiler.util.StringStack;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.thoughtworks.xstream.io.path.Path;

/**
 * 中缀表达式转变为前缀表达式和后缀表达式：实现万能计算器 (2+1)*3 后缀表达式：2 1 + 3 * 前缀表达式：* + 2 1 3
 * 1+(2*4-2/3+(2/2+1*(4-2*4/2+(10/2*4-1))))+(43/2-1)/2-2 1+(2*3/2-1)+(2+3)/1+1
 * 1+(2*3/2-1)+((2+3)/1)+1 1+(2*3/2-1)+(10/(2+3))+1 1+(2+3+(1+2/(3-4*(1-2))))
 * 1+(2+3+(1+(2/(3-(4*(1-2)))))) 1+(2*4/2+2-1)+(4/2*2-(2-4))*2-(1-0)
 *
 * @author think
 * @version 1.0.0
 * @since 2019/12/22
 */
public class Calculator {

    private static final String TERMINAL_STR = "BYE";

    private static final String MAX = "MAX";

    private static final String MIN = "MIN";

    private static final String MOD = "MOD";

    private static final String SINGLE1 = "(";

    private static final String SINGLE2 = ")";

    private static final String SINGLE3 = "*";

    private static final String SINGLE4 = "/";

    private static final String SINGLE5 = ",";

    private static final String SINGLE6 = "+";

    private static final String SINGLE7 = "-";

    /**
     * 中缀表达式转变为后缀表打死
     *
     * @param args 参数
     */
    public static void main(String[] args) {
        // Scanner scanner = new Scanner(System.in);
        // String line;
        // while (!(line = scanner.nextLine()).equalsIgnoreCase(TERMINAL_STR)) {
        // line = line.replaceAll("\\s", "");
        String line = "1+(2+3)*2/3+MAX(2,3)";
        System.out.println(Arrays.toString(toPrefixStr(toSepStrs2(line))));
        System.out.println(expressCalsForPreffix(toPrefixStr(toSepStrs2(line))));
        System.out.println(Arrays.toString(toSuffixStr(toSepStrs2(line))));
        System.out.println(expressCalcForSuffix(toSuffixStr(toSepStrs2(line))));
        // }
        String str = "1+(2+3)*2/3+MAX(2,3)";
        System.out.println(Arrays.toString(toSepStrs(str)));
        System.out.println(Arrays.toString(toSepStrs2(str)));
    }

    /**
     * 前缀表达式计算 注：前缀表达式计算跟后缀表达式计算需要区分的地方：减法和除法运算符
     *
     * @param strs 输入
     * @return 计算结果
     */
    private static int expressCalsForPreffix(String[] strs) {
        Stack<Integer> stack = new Stack<>();
        int len = strs.length;
        for (int i = len - 1; i >= 0; i--) {
            String str = strs[i];
            switch (str) {
                case "+":
                    stack.push(EnumOperation.PLUS.apply(stack.pop(), stack.pop()));
                    break;
                case "-":
                    int num1 = stack.pop();
                    int num2 = stack.pop();
                    stack.push(EnumOperation.MINUS.apply(num1, num2));
                    break;
                case "*":
                    stack.push(EnumOperation.MULTY.apply(stack.pop(), stack.pop()));
                    break;
                case "/":
                    num1 = stack.pop();
                    num2 = stack.pop();
                    stack.push(EnumOperation.DIV.apply(num1, num2));
                    break;
                case MOD:
                    num1 = stack.pop();
                    num2 = stack.pop();
                    stack.push(num1 % num2);
                    break;
                case MAX:
                    stack.push(EnumOperation.MAX.apply(stack.pop(), stack.pop()));
                    break;
                case MIN:
                    stack.push(EnumOperation.MIN.apply(stack.pop(), stack.pop()));
                    break;
                default:
                    stack.push(Integer.parseInt(str));
                    break;
            }
        }
        return stack.pop();
    }

    /**
     * 后缀表达式计算 注：前缀表达式计算跟后缀表达式计算需要区分的地方：减法和除法运算符
     *
     * @param strs 字符串数组
     * @return 计算结果
     */
    private static int expressCalcForSuffix(String[] strs) {
        Stack<Integer> stack = new Stack<>();
        for (String str : strs) {
            switch (str) {
                case "+":
                    stack.push(EnumOperation.PLUS.apply(stack.pop(), stack.pop()));
                    break;
                case "-":
                    int num1 = stack.pop();
                    int num2 = stack.pop();
                    stack.push(EnumOperation.MINUS.apply(num2, num1));
                    break;
                case "*":
                    stack.push(EnumOperation.MULTY.apply(stack.pop(), stack.pop()));
                    break;
                case "/":
                    num1 = stack.pop();
                    num2 = stack.pop();
                    stack.push(EnumOperation.DIV.apply(num2, num1));
                    break;
                case MOD:
                    num1 = stack.pop();
                    num2 = stack.pop();
                    stack.push(num2 % num1);
                    break;
                case MAX:
                    stack.push(EnumOperation.MAX.apply(stack.pop(), stack.pop()));
                    break;
                case MIN:
                    stack.push(EnumOperation.MIN.apply(stack.pop(), stack.pop()));
                    break;
                default:
                    stack.push(Integer.parseInt(str));
                    break;
            }
        }
        return stack.pop();
    }

    /**
     * 中缀表达式转变为前缀表达式 注：如果当前运算符的优先级 大于或者等于 栈顶的优先级则压入堆栈，否则弹出当前栈顶的运算符
     *
     * @param strs 拆解之后的字符串数组
     * @return 前缀表达式
     */
    private static String[] toPrefixStr(String[] strs) {
        StringStack stringStack = new StringStack();
        List<String> result = new ArrayList<>();
        int len = strs.length;
        for (int i = len - 1; i >= 0; i--) {
            String str = strs[i];
            switch (str) {
                case "+":
                case "-":
                    // 当前运算符的优先级是最低的
                    while (!stringStack.isEmpty() && !SINGLE2.equals(stringStack.peekString())
                            && (!SINGLE6.equals(stringStack.peekString())
                                    || !SINGLE7.equals(stringStack.peekString()))) {
                        result.add(stringStack.popString());
                    }
                    stringStack.pushString(str);
                    break;
                case "*":
                case "/":
                case MAX:
                case MIN:
                case MOD:
                case SINGLE2:
                    // 优先级是最高的，所以直接压入堆栈
                    stringStack.pushString(str);
                    break;
                case SINGLE1:
                    // ()成对出现，直接把括号中间的所有运算符弹出即可，可能含有逗号(逗号不压入)
                    while (!stringStack.isEmpty() && !SINGLE2.equals(stringStack.peekString())) {
                        if (SINGLE5.equals(stringStack.peekString())) {
                            stringStack.popString();
                            continue;
                        }
                        result.add(stringStack.popString());
                    }
                    if (!stringStack.isEmpty() && stringStack.peekString().equals(SINGLE2)) {
                        stringStack.popString();
                    }
                    break;
                case ",":
                    // 逗号符号一定是出现如MAX/MIN/MOD或者自定义函数中，并且一定是在括号中间
                    while (!stringStack.isEmpty() && !SINGLE2.equals(stringStack.peekString())) {
                        result.add(stringStack.popString());
                    }
                    break;
                default:
                    result.add(str);
                    break;
            }
        }
        while (!stringStack.isEmpty()) {
            result.add(stringStack.popString());
        }
        Collections.reverse(result);
        return result.toArray(new String[] {});
    }

    /**
     * 中缀表达式转变为后缀表达式 注：如果当前运算符的优先级 大于 栈顶的优先级则压入堆栈，否则弹出当前栈顶的运算符
     *
     * @param strs 拆解之后的字符串数组
     * @return 后缀表达式
     * @date 2011/11/11
     * @since 2019/11/11
     */
    private static String[] toSuffixStr(String[] strs) {
        StringStack stringStack = new StringStack();
        List<String> result = new ArrayList<>();
        for (String str : strs) {
            switch (str) {
                case "+":
                case "-":
                    // 当前运算符的优先级是最低的
                    while (!stringStack.isEmpty() && !SINGLE1.equals(stringStack.peekString())) {
                        result.add(stringStack.popString());
                    }
                    stringStack.pushString(str);
                    break;
                case "*":
                case "/":
                    // 当前运算符最高运算符，同优先级的运算符都需要弹出
                    while (!stringStack.isEmpty()
                            && (SINGLE3.equals(stringStack.peekString()) || SINGLE4.equals(stringStack.peekString()))) {
                        result.add(stringStack.popString());
                    }
                    stringStack.pushString(str);
                    break;
                case MAX:
                case MIN:
                case MOD:
                    // 当前运算符最高优先级，同优先级的运算符都需要弹出
                    while (!stringStack.isEmpty() && (MAX.equals(stringStack.peekString())
                            || MIN.equals(stringStack.peekString()) || MOD.equals(stringStack.peekString()))) {
                        result.add(stringStack.popString());
                    }
                    stringStack.pushString(str);
                    break;
                case SINGLE2:
                    // ()成对出现，直接把括号中间的所有运算符弹出即可，可能含有逗号(逗号不压入)
                    while (!stringStack.isEmpty() && !SINGLE1.equals(stringStack.peekString())) {
                        if (SINGLE5.equals(stringStack.peekString())) {
                            stringStack.popString();
                            continue;
                        }
                        result.add(stringStack.popString());
                    }
                    if (!stringStack.isEmpty() && stringStack.peekString().equals(SINGLE1)) {
                        stringStack.popString();
                    }
                    break;
                case ",":
                    // 逗号符号一定是出现如MAX/MIN/MOD或者自定义函数中，并且一定是在括号中间
                    while (!stringStack.isEmpty() && !SINGLE1.equals(stringStack.peekString())) {
                        result.add(stringStack.popString());
                    }
                    break;
                case SINGLE1:
                    stringStack.pushString(str);
                    break;
                default:
                    result.add(str);
                    break;
            }
        }
        while (!stringStack.isEmpty()) {
            result.add(stringStack.popString());
        }
        return result.toArray(new String[] {});
    }

    /**
     * 字符串拆解为字符串数组
     *
     * @param str 原始字符串
     * @return 字符串数组
     */
    private static String[] toSepStrs(String str) {
        int len = str.length();
        CharSequence charSequence = str.subSequence(0, len);
        StringBuilder stringBuilder = new StringBuilder();
        List<String> result = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            if (i + MAX.length() < len && charSequence.subSequence(i, i + MAX.length()).equals(MAX)) {
                result.add(MAX);
                i = i + MAX.length() - 1;
            } else if (i + MIN.length() < len && charSequence.subSequence(i, i + MIN.length()).equals(MIN)) {
                result.add(MIN);
                i = i + MIN.length() - 1;
            } else if (i + MOD.length() < len && charSequence.subSequence(i, i + MOD.length()).equals(MOD)) {
                result.add(MOD);
                i = i + MOD.length() - 1;
            } else if (!StringUtils.isNumeric(charSequence.subSequence(i, i + 1))) {
                if (stringBuilder.length() > 0) {
                    result.add(stringBuilder.toString());
                }
                result.add(String.valueOf(charSequence.subSequence(i, i + 1)));
                stringBuilder.delete(0, stringBuilder.length());
            } else {
                if (i == len - 1) {
                    result.add(charSequence.subSequence(i, i + 1).toString());
                } else {
                    stringBuilder.append(charSequence.subSequence(i, i + 1));
                }
            }
        }
        return result.toArray(new String[] {});
    }

    private static String[] toSepStrs2(String str) {
        Pattern pattern = Pattern.compile("\\d|\\+|-|\\*|/|MOD|,|\\(|\\)|MAX|MIN");
        Matcher matcher = pattern.matcher(str);
        List<String> result = new ArrayList<>();
        while (matcher.find()) {
            int start = matcher.start();
            int end = matcher.end();
            result.add(str.substring(start, end).trim());
        }
        return result.toArray(new String[] {});
    }
}
